# @Author:黄景涛
# @Date: 2023/10/9
import asyncio
import json
import time

import re
import random

import requests
from pypinyin import lazy_pinyin
from settings import fk, create_organization, Node, dfs
from collections import deque


class Organizate_Error(Exception):
    """组织校验的错误"""


class User_Error(Exception):
    """用户校验的错误"""


# 域名
base_url = "http://paas.test.com"

# 请求头配置，登录过期后，需要手动更新下，这里只能用admin的登录信息，因为只有它有权限创建
headers = {
    "User-Agent": fk.user_agent(),
    "Cookie": "blueking_language=zh-cn; bk_csrftoken=cEY8FEM77Jp41Eq2ktSsyImZ5LstqAPMNre02AvjS7ADMw6DgWkZziv0e3yVZMey; bkuser_csrftoken=4jbbfYd6Pl6fsQbgRrm7cQeoTWezmBLsUekwrCzYbLsmDf5az5HrLogWZtIuHzWF; o_s=%7B%22lock%22%3Afalse%2C%22msg%22%3A%7B%7D%7D; bk_token=doCC920ks260_D4a0rA7HXq6AKPmeMCounq5ZgGV-HM; bklogin_csrftoken=XQaeZlpXNd25uLKnplL40wjTuHOXy0cWv6DKuYxucDFMQzyFbTy362RxtoG3kw3P; o_f=%7B%22lock%22%3Atrue%2C%22msg%22%3A%7B%22workbench%22%3A%7B%222023-10-11%22%3A3612%7D%7D%7D; sessionid=eqcqf8la0ueb939nxz0lchjx67w5vi62",
    "X-Csrftoken": "4jbbfYd6Pl6fsQbgRrm7cQeoTWezmBLsUekwrCzYbLsmDf5az5HrLogWZtIuHzWF"
}

# 并发数量控制
CONCURRENT_LIMIT = 80
semaphore = asyncio.Semaphore(CONCURRENT_LIMIT)

# full_url
full_organizate_url = base_url + '/o/bk_user_manage/api/v1/web/departments/'
full_user_url = base_url + '/o/bk_user_manage/api/v1/web/profiles/'


def _parser_id(res_data: dict) -> int:
    """
    解析响应结果中，组织id
    :param res_data: 请求的响应json
    :return: 组织id
    """
    pattern = "'id': (.*?),"
    organizate_id = re.search(pattern, str(res_data)).group(1)
    return int(organizate_id)


def _update_organizate_structure(target_node: Node, organizate_id: int):
    """
    更新节点的属性id
    :param target_node: 目标组织
    :param organizate_id: 组织id
    :return: None
    """
    target_node.id = organizate_id


# 任务1
def add_organizate(target_node: Node, name: str, category_id: int = 1, parent=None):
    """
    1.发起异步请求：新增组织； 2.更新组织字典（往原组织名中，补充组织id，用于后续的用户添加）
    :param target_node: 目标组织
    :param name: 组织名称
    :param category_id: 类型
    :param parent: 父级组织id
    :return: None
    """
    # 参数判断
    data = {
        "name": name,
        "category_id": category_id,
    }

    if parent is not None:
        data.update(parent=parent)

    print(f'{name}的参数：{data}')

    try:
        resp = requests.post(full_organizate_url, json=data, headers=headers)
        res_data = resp.json()
        print(f'新增组织{name}响应结果：{res_data}')
        if res_data.get('code') == -1:
            msg = f'{name}新增失败 >>{res_data.get("message")}'
            raise Organizate_Error(msg)
    except Exception as e:
        msg = f'错误：{str(e)[:50]}'
        raise Exception(msg)

    # 提取响应中组织id
    organizate_id = _parser_id(res_data)
    # 更新目标字典中，组织的信息(添加组织id)
    _update_organizate_structure(target_node, organizate_id)


# 任务2
def add_user(success_set: set, target_node: Node, category_id: int = 1):
    """
    新增用户
    :param success_set: 用户名的集合，用于过滤重复的用户名
    :param target_node: 目标组织
    :param category_id: 组织类型
    :return: None
    """
    # 参数构建
    # 用户名规则：由1-32位字母、数字、下划线_、点.、减号-组成，已字母或数字开头
    character_pool = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_"
    # 中文名：随机姓名+后缀
    display_name = fk.name() + ''.join(random.choices(character_pool, k=random.randint(0, 5)))
    # 姓名拼音+后缀
    pinyin = ''.join(lazy_pinyin(display_name))
    print(pinyin)
    while True:
        username = pinyin + ''.join(random.choices(character_pool, k=random.randint(2, 31-len(pinyin))))
        if username not in success_set:
            success_set.add(username)
            break
    # 性别：0, 男；1，女
    sex_choice = (0, 1)
    # 账户状态，大概率取NORMAL：正常，小概率取其他
    status_choice = ("NORMAL", "LOCKED", "DELETED", "DISABLED", "EXPIRED")
    # 在职状态：大概率取IN: 在职，小概率取离职
    staff_status_choice = ("IN", "OUT")

    data = {
        "category_id": category_id,
        "leader": [],
        "departments": [target_node.id],  # 组织
        "password_valid_days": -1,  # 密码有效期，-1表示永久
        "username": username,  # 用户名，唯一
        "display_name": display_name,  # 用户全名，可以相同
        "email": fk.free_email(),  # 邮箱
        "telephone": fk.phone_number(),  # 手机号
        "iso_code": "cn",
        "status": ''.join(random.choices(status_choice, (0.9, 0.025, 0.025, 0.025, 0.025), k=1)),  # 账户状态
        "staff_status": ''.join(random.choices(staff_status_choice, (0.9, 0.1), k=1)),  # 在职状态
        "position": "0",
        "wx_userid": random.choice(("", "weixin" + ''.join(random.choices(character_pool, k=10)))),  # 微信
        "qq": random.choice(("", ''.join(random.choices(character_pool[:10], k=random.randint(6, 12))))),  # qq
        "account_expiration_date": "",  # 账号到期时间
        "sex": random.choice(sex_choice)  # 性别
    }

    try:
        resp = requests.post(full_user_url, json=data, headers=headers)
        res_data = resp.json()
        print(f'{display_name}的响应结果：{res_data}')
        if res_data.get('code') == -1:
            msg = f'{username}新增失败，错误：{res_data.get("message")}'
            raise User_Error(msg)
    except Exception as e:
        msg = f'{username}新增失败，错误：{str(e)[:50]}'
        raise Exception(msg)


# 树结构：层序遍历（BFS），边创建组织，边在组织下创建用户
def create_task(success_set: set, current_node: Node):
    """
    新增用户
    :param success_set: 用户名的集合，用于过滤重复的用户名
    :param current_node: 组织节点
    :return: None
    """
    if current_node.name == 'root':
        return

    # 1.新增组织的请求
    parent_node = current_node.parent
    if parent_node.name == 'root':
        add_organizate(current_node, current_node.name)
    else:
        print(parent_node.id, parent_node.name)
        add_organizate(current_node, current_node.name, parent=parent_node.id)

    # 2.新增用户的请求
    user_limit = current_node.user_limit
    for i in range(user_limit):
        add_user(success_set, current_node)


# 树结构：层序遍历（BFS），边创建组织，边在组织下创建用户
def engine(root: Node):
    """
    层序遍历（BFS）：每遍历一层级组织，一边创建组织，一边创建组织下的用户
    :param root: 组织架构树的根节点，虚拟节点
    :return: None
    """
    # 1.层序遍历
    queue = deque()
    queue.append(root)
    # 构造用户名的过滤器，防止用户名重复
    success_set = set()

    # BFS
    while queue:
        # 从队列左侧出队，一个节点
        current_node = queue.popleft()
        """节点的处理操作"""
        create_task(success_set, current_node)
        # 将当前节点的子节点，入队
        for child in current_node.childrens:
            queue.append(child)


if __name__ == '__main__':
    start_time = time.time()
    # 启动协程
    root = create_organization()
    # engine(root)
    counts = 0
    dfs(root)
    print(f'总数：{dfs.num_calls}')
    print(f'耗时：{time.time() - start_time}')

